package com.cskaoyan.javase.oop2._1extends._8hidden;

/**
 * 继承中属性的隐藏
 * 在上面章节中，我们已经知道，父子类的成员是允许同名的。那么如果父子类属性同名了，那么不使用super区分，直接访问的效果是什么样的呢？
 *
 * 现在我们直接创建子类对象，使用子类引用接收。会发现访问的结果是子类中属性的取值，那么父类中的那个成员变量去哪了呢？首先它肯定是存在的，这一点随便写一个父类成员变量的Getter方法就可以验证了。
 * 这说明使用 "对象名点"访问成员变量 这个语法，可能存在一些特殊的机制，接下来我们就来探讨这种机制。
 *
 * 我们创建子类引用指向子类对象
 *      使用"对象名点"访问父子类同名成员变量,父类中的同名成员变量是访问不到的,体现为"属性的隐藏"
 *
 * 接下来主要研究"对象名点"成员变量的访问机制:
 * 在研究 对象名点访问成员变量 机制时，我们主要探究两个方向：
 *      1.访问范围，究竟能够访问到哪些成员变量？根据什么来决定？
 *      2.访问结果，如果是父子类同名成员变量，那么结果是什么？根据什么来决定？
 *
 * 对象名点访问成员变量,对象的由来，可以有三种方式：
 *      1.创建父类对象,用父类引用接收，用对象名点访问。
 *      2.创建子类对象,用子类引用接收，用对象名点访问。
 *      3.创建子类对象,用父类引用接收，用对象名点访问。
 *
 * 首先研究访问范围,研究对象名点成员变量究竟能够访问哪些成员变量:
 *      1.创建父类对象,用父类引用接收，用对象名点访问。
 *      访问范围仅限于 父类
 *      不能访问子类中的成员变量
 *
 *      2.创建子类对象,用子类引用接收，用对象名点访问。
 *      访问范围是 父类 + 子类
 *      因为继承,子类得到了父类的成员
 *
 * 3.创建子类对象,用父类引用接收，用对象名点访问。
 *      访问范围也仅限于 父类
 *
 *  以上,"对象名点"成员变量的访问范围,要看引用的数据类型:
 *      1.如果引用本身就是一个父类,那么访问范围就仅限于父类
 *      2.如果引用本身是一个子类,那么访问访问范围是子类 + 父类
 *
 *  扩展:
 *      1.访问范围是一个编译上的概念,编译时期就能够确定能否访问,而引用的数据类型声明本声就是告诉编译器数据类型,对于编译器而言,不知道这个对象的类型,但是知道引用的类型.
 *      所以编译器只能通过引用的数据类型来决定访问范围. 访问范围只需要关注引用的数据类型即可.
 *
 *      2.编译器在编译时,是能够明确知道这个类的父类的
 *          所以在查找成员变量时,子类类型的引用可以访问"父类 + 子类"
 *          但是,编译器是无从得知一个类具有子类的,所以在查找成员变量时,不可能去找子类.
 *
 *   编译器在查找访问范围时,会从自身类型开始查找,如果找不到,就去父类类型中找,直到找到Object类,如果还没有找到,说明根本不存在这个成员,会编译报错
 *
 *
 *  接下来研究访问结果,研究对象名点成员变量的访问结果
 *   1.创建父类对象,用父类引用接收，用对象名点访问。
 *      体现出父类中的结果
 *   2.创建子类对象,用子类引用接收，用对象名点访问。
 *      体现出子类中的结果
 *   3.创建子类对象,用父类引用接收，用对象名点访问。
 *      体现出父类中的结果
 *
 *      以上总结,对象名点成员变量的访问结果,看引用就可以了,要体现出引用中类型的结果
 *
 * 综合对象名点访问成员变量的范围和结果，发现：
 *      都是根据引用的数据类型来决定的，无需考虑是何种对象。
 *
 * 思考:
 *      父类引用指向子类对象
 *      和之前子类引用指向子类对象有什么区别?
 *
 *      访问范围是一个编译概念,父类引用会限制子类对象的访问范围
 *          父类引用指向子类对象
 *          就无法访问子类独有成员了.
 *
 * @since 17:33
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        /*// 1.创建父类对象,用父类引用接收，用对象名点访问。
        Father f = new Father();
        System.out.println(f.fatherVar);
        System.out.println(f.num);
        // f.son
        // 2.创建子类对象,用子类引用接收，用对象名点访问。
        Son s = new Son();
        System.out.println(s.sonVar);
        System.out.println(s.fatherVar);
        // 3.创建子类对象,用父类引用接收，用对象名点访问。
        Father fs = new Son();
        System.out.println(fs.num);
        System.out.println(fs.fatherVar);
        // fs.sonVar*/

        //1.创建父类对象,用父类引用接收，用对象名点访问。
        Father f = new Father();
        System.out.println(f.num);

        //  2.创建子类对象,用子类引用接收，用对象名点访问。
        Son s = new Son();
        System.out.println(s.num);
        //  3.创建子类对象,用父类引用接收，用对象名点访问。
        Father fs = new Son();
        System.out.println(fs.num);

    }
}

class Father {
    int num = 10;
    int fatherVar = 20;

    public int getNum() {
        return num;
    }
}

class Son extends Father {
    int num = 100;
    int sonVar = 200;
}